/*
 * Copyright 2010-2011 CGT&DC ISA RAS
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package websolve.workflow.wfms;

import java.io.File;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Logger;

import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONObject;

import websolve.everest.JobStatus;
import websolve.everest.adapter.java.JavaServiceI;
import websolve.workflow.runtime.Task;
import websolve.workflow.runtime.TaskState;
import websolve.workflow.runtime.Workflow;
import websolve.workflow.util.ErrorUtils;

public class WorkflowService implements JavaServiceI {

    private String workflowId;
    private JSONObject workflowDesc;
    private WorkflowManager wfMan;
    private String baseURI;

    public WorkflowService(String workflowId, JSONObject workflowDesc, WorkflowManager wfMan, String baseURI) {
        this.workflowId = workflowId;
        this.workflowDesc = workflowDesc;
        this.wfMan = wfMan;
        this.baseURI = baseURI;
    }

    @Override
    public void init(Map<String, String> arg0) {
        // Does nothing
    }

    @Override
    public void run(Map<String, Object> inParams, Map<String, Object> outParams, String jobId, String jobDir,
            JobStatus status) throws Exception {
        try {
            log.info("Workflow execution request (workflow: " + workflowId + ", job: " + jobId + ")");

            // TODO Remove this hack!
            String user = (String)inParams.remove("everest.request.user");
            
            // create workflow
            Workflow workflow = JSONParser.parseWorkflow(workflowDesc, user);

            // set input params
            for (Entry<String, Object> e : inParams.entrySet()) {
                if (e.getValue() instanceof File) {
                    File file = (File) e.getValue();
                    String fileURI = baseURI + "/job" + jobId + "/" + file.getName();
                    // set file URI as param value
                    workflow.setInputValue(e.getKey(), fileURI);
                    log.fine("Set input value: " + e.getKey() + " = " + fileURI);
                } else {
                    workflow.setInputValue(e.getKey(), e.getValue());
                    log.fine("Set input value: " + e.getKey() + " = " + e.getValue());
                }
            }

            // run workflow
            wfMan.addInstance(workflowId, jobId, workflow);
            wfMan.runInstance(workflowId, jobId);
            log.info("Workflow instance submitted (workflow: " + workflowId + ", job: " + jobId + ")");

            // check state
            String state;
            do {
                Thread.sleep(1000);
                state = workflow.getState();
                status.setInfo("Workflow state: " + state);
                log.fine("Workflow " + workflowId + " / " + jobId + " state: " + state);

                // extra info
                JSONObject extraInfo = new JSONObject();
                extraInfo.put("workflowId", workflowId);
                extraInfo.put("start-time", workflow.getStartTime());
                extraInfo.put("finish-time", workflow.getFinishTime());

                JSONArray tasks = new JSONArray();
                for (Task task : workflow.getTasks()) {
                    tasks.put(new JSONObject().put("name", task.getName()).put("state", task.getState())
                            .put("info", task.getInfo()).put("error", task.getError())
                            .put("start-time", task.getStartTime()).put("finish-time", task.getFinishTime()));
                }
                extraInfo.put("tasks", tasks);

                // outputs
                for (Entry<String, Object> e : workflow.getOutputValues().entrySet()) {
                    if (e.getValue() != null) {
                        outParams.put(e.getKey(), e.getValue());
                    }
                }

                status.setExtraInfo(extraInfo);
                log.fine("Workflow " + workflowId + " / " + jobId + " extra info: " + extraInfo);

            } while (state.equals(TaskState.RUNNING));

            log.fine("Workflow " + workflowId + " / " + jobId + " is not RUNNING");

            // check results
            if (state.equals(TaskState.DONE)) {
                // // read output params
                // Map<String,Object> outputs = new HashMap<String,Object>();
                // for (Entry<String,Object> e :
                // workflow.getOutputValues().entrySet()) {
                // if (workflow.getOutput(e.getKey()).equals("file")) {
                //
                // } else {
                // outputs.put(e.getKey(), e.getValue());
                // }
                // }
                log.info("Workflow " + workflowId + " / " + jobId + " is DONE");
            } else {
                status.setInfo("Workflow execution failed");
                log.severe("Workflow " + workflowId + " / " + jobId + " execution failed");
                throw new RuntimeException("Workflow execution failed");
            }
        } catch (InterruptedException e) {
            log.severe(ErrorUtils.stackTraceToString(e));
            log.severe("Cancelling workflow " + workflowId + " / " + jobId + " execution");
            wfMan.removeInstance(workflowId, jobId);
            throw e;
        }
    }

    @Override
    public void destroy() {
        // TODO
    }

    private static final Logger log = Logger.getLogger(WorkflowService.class.getName());

}
